class Bind {
  constructor({ el, data, events, asyncData }) {
    if (typeof el === 'string') {
      this.el = document.querySelector(el)
    } else if (el instanceof window.HTMLElement) {
      this.el = el
    } else {
      this.el = document.body
    }
    this.renderTemplate = ejs.compile(/* this.el.innerText */this.el.innerHTML?.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&').replaceAll('&quot;', '"').replaceAll('&apos;', "'") || '')
    this.bind = events
    this.setData(data)
    this.el.hidden = false
    this.asyncData = asyncData
    this.refresh()
  }

  refresh() {
    if (typeof this.asyncData === 'function') {
      this.asyncData().then(data => {
        this.setData(data)
      })
    }
  }

  querySelector(selector) {
    return this.el.querySelector(selector)
  }

  render() {
    this.el.innerHTML = this.renderTemplate(this.data)
  }

  setData(data, newFlag) {
    this.data = newFlag ? data : { ...this.data, ...data }
    this.render()
  }

  on(eventName, handlers = {}) {
    const handler = (e) => {
      Object.entries(handlers).forEach(([selector, callback]) => {
        const target = e.target.closest(selector)
        if (target) {
          callback?.call(this, e.type === "click" ? { ...e, target } : e)
        }
      })
    }
    this.el.addEventListener(eventName, handler)
  }
  set bind(events = []) {
    Object.entries(events).forEach(([eventName, handlers]) => {
      this.on(eventName, handlers)
    })
  }
}